#===============================================================================
# Copyright 2021 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#===============================================================================

# It defines the following variables:
#     oneDAL_<component>_FOUND
#     oneDAL_IMPORTED_TARGETS
#
# oneDALConfigVersion.cmake defines oneDAL_VERSION

if(oneDAL_FOUND)
    return()
endif()

# Handle arguments:
# ONEDAL_LINK: static | dynamic. Default is dynamic
# ONEDAL_USE_DPCPP: yes | no. Default is yes
# ONEDAL_INTERFACE: yes | no. Default is no

# Internal variables:
# ONEDAL_SET_TBB_MANUALLY: yes | no. Default is no
# MKL_DEPENDENCY: yes | no. Default is no
# MATH_BACKEND: mkl | ref. Default is mkl
unset(DAL_LIBS)

# default CMAKE_BUILD_TYPE to Release
if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type not specified, defaulting to Release" FORCE)
endif()

if(WIN32)
    message(STATUS "Optimization library is not enabled on Windows")
    set(ONEDAL_USE_PARAMETERS_LIBRARY no)
else()
    set(ONEDAL_USE_PARAMETERS_LIBRARY yes)
endif()

#Backward compatibility section to be removed with next major version
if(NOT DEFINED ONEDAL_LINK AND DEFINED TARGET_LINK)
  message(STATUS "TARGET_LINK is deprecated. Please use ONEDAL_LINK instead.")
  set(ONEDAL_LINK ${TARGET_LINK})
endif()
if(NOT DEFINED ONEDAL_USE_DPCPP AND DEFINED USE_DPCPP)
  message(STATUS "USE_DPCPP is deprecated. Please use ONEDAL_USE_DPCPP instead.")
  set(ONEDAL_USE_DPCPP ${USE_DPCPP})
endif()
if(NOT DEFINED ONEDAL_INTERFACE AND DEFINED USE_NEW_IFACES)
  message(STATUS "USE_NEW_IFACES is deprecated. Please use ONEDAL_INTERFACE instead.")
  set(ONEDAL_INTERFACE ${USE_NEW_IFACES})
endif()
if(NOT DEFINED ONEDAL_SET_TBB_MANUALLY AND DEFINED SET_TBB_MANUALLY)
  message(STATUS "SET_TBB_MANUALLY is deprecated. Please use ONEDAL_SET_TBB_MANUALLY instead.")
  set(ONEDAL_SET_TBB_MANUALLY ${SET_TBB_MANUALLY})
endif()

if ("${REF_BACKEND}" STREQUAL "" OR "${REF_BACKEND}" STREQUAL "OFF")
    set(MATH_BACKEND "mkl")
elseif ("${REF_BACKEND}" STREQUAL "ON")
    set(MATH_BACKEND "ref")
elseif (NOT "${REF_BACKEND}" STREQUAL "OFF" AND NOT "${REF_BACKEND}" STREQUAL "ON")
    message(FATAL_ERROR "REF_BACKEND must be 'ON' or 'OFF'")
endif()

if ("${ONEDAL_LINK}" STREQUAL "" OR "${ONEDAL_LINK}" STREQUAL "dynamic")
    set(ONEDAL_LINK "dynamic")
    set(MKL_DEPENDENCY "no")
elseif ("${ONEDAL_LINK}" STREQUAL "static")
    set(ONEDAL_LINK "static")
    if("${MATH_BACKEND}" STREQUAL "mkl")
        set(MKL_DEPENDENCY "yes")
    elseif("${MATH_BACKEND}" STREQUAL "ref")
        set(MKL_DEPENDENCY "no")
    endif()
elseif (NOT "${ONEDAL_LINK}" STREQUAL "static" AND NOT "${ONEDAL_LINK}" STREQUAL "dynamic")
    message(FATAL_ERROR "ONEDAL_LINK must be 'static' or 'dynamic'")
endif()
if ("${ONEDAL_USE_DPCPP}" STREQUAL "")
    set(ONEDAL_USE_DPCPP "yes")
elseif (NOT "${ONEDAL_USE_DPCPP}" STREQUAL "yes" AND NOT "${ONEDAL_USE_DPCPP}" STREQUAL "no")
    message(FATAL_ERROR "ONEDAL_USE_DPCPP must be 'yes' or 'no'")
endif()
if ("${ONEDAL_INTERFACE}" STREQUAL "")
    set(ONEDAL_INTERFACE "no")
elseif (NOT "${ONEDAL_INTERFACE}" STREQUAL "yes" AND NOT "${ONEDAL_INTERFACE}" STREQUAL "no")
    message(FATAL_ERROR "ONEDAL_INTERFACE must be 'yes' or 'no'")
endif()
if ("${ONEDAL_SET_TBB_MANUALLY}" STREQUAL "")
    set(ONEDAL_SET_TBB_MANUALLY "no")
elseif (NOT "${ONEDAL_SET_TBB_MANUALLY}" STREQUAL "yes" AND NOT "${ONEDAL_SET_TBB_MANUALLY}" STREQUAL "no")
    message(FATAL_ERROR "ONEDAL_SET_TBB_MANUALLY must be 'yes' or 'no'")
endif()
if(WIN32 AND CMAKE_BUILD_TYPE MATCHES ".*Debug.*|.*DebInfo.*")
    set(DAL_DEBUG_SUFFIX "d")
else()
    set(DAL_DEBUG_SUFFIX "")
endif()

message(STATUS "CMAKE_BUILD_TYPE:        ${CMAKE_BUILD_TYPE}")
message(STATUS "ONEDAL_LINK:             ${ONEDAL_LINK}")
message(STATUS "ONEDAL_USE_DPCPP:        ${ONEDAL_USE_DPCPP}")
message(STATUS "ONEDAL_INTERFACE:        ${ONEDAL_INTERFACE}")
message(STATUS "ONEDAL_SET_TBB_MANUALLY: ${ONEDAL_SET_TBB_MANUALLY}")
message(STATUS "MATH_BACKEND:            ${MATH_BACKEND}")
message(STATUS "REF_BACKEND:             ${REF_BACKEND}")
message(STATUS "MKL_DEPENDENCY:          ${MKL_DEPENDENCY}")
message(STATUS "ONEDAL_USE_DPCPP:        ${ONEDAL_USE_DPCPP}")

if (NOT DAL_LIBRARIES)
    set(DAL_LIBRARIES "")
endif()
set(oneDAL_IMPORTED_TARGETS "")

get_filename_component(_dal_root "${CMAKE_CURRENT_LIST_DIR}" REALPATH)
get_filename_component(_dal_root "${_dal_root}/@DAL_ROOT_REL_PATH@" ABSOLUTE)

if (@VERSIONS_SET@ STREQUAL "TRUE")
    set(_dal_ver_major_bin @DAL_VER_MAJOR_BIN@)
    set(_dal_ver_minor_bin @DAL_VER_MINOR_BIN@)
elseif (@VERSIONS_SET@ STREQUAL "FALSE")
    # version_info.h is set according to the relevant package structure
    set(version_info.h ${_dal_root}/include/services/library_version_info.h)
    if (NOT EXISTS "${version_info.h}")
        set(version_info.h ${_dal_root}/include/dal/services/library_version_info.h)
    endif()
    file(READ ${version_info.h} DAL_VERSION_INFO)
    string(REGEX REPLACE ".*#define __INTEL_DAAL_MAJOR_BINARY__ ([0-9]+).*" "\\1" _dal_ver_major_bin "${DAL_VERSION_INFO}")
    string(REGEX REPLACE ".*#define __INTEL_DAAL_MINOR_BINARY__ ([0-9]+).*" "\\1" _dal_ver_minor_bin "${DAL_VERSION_INFO}")
endif()

# The mkl_core part is required for compatibility with different MKL versions
# at runtime and during the oneDAL build process. For more details on the
# static build structure, search for 'Link static lib' in the 'common.mk'.
if(MKL_DEPENDENCY STREQUAL "yes")
    set(MKL_THREADING "tbb_thread")
    set(MKL_SYCL_LINK "static")
    set(MKL_LINK "static")
    set(MKL_SYCL_INTERFACE_FULL "intel_ilp64")
    set(MKL_INTERFACE_FULL "intel_ilp64")
    find_package(MKL REQUIRED MKL::MKL_SYCL MKL::MKL_CORE)
    list(APPEND oneDAL_IMPORTED_TARGETS MKL::mkl_core MKL::mkl_intel_ilp64 MKL::mkl_tbb_thread${DAL_DEBUG_SUFFIX})
endif()

if (ONEDAL_USE_DPCPP STREQUAL "yes" AND ONEDAL_INTERFACE STREQUAL "no")
    if(MKL_DEPENDENCY STREQUAL "yes")
        list(APPEND oneDAL_IMPORTED_TARGETS MKL::mkl_sycl${DAL_DEBUG_SUFFIX})
    endif()
elseif (ONEDAL_USE_DPCPP STREQUAL "no" AND ONEDAL_INTERFACE STREQUAL "yes")
    list(APPEND DAL_LIBS onedal${DAL_DEBUG_SUFFIX})
    if(ONEDAL_USE_PARAMETERS_LIBRARY)
        list(APPEND DAL_LIBS onedal_parameters${DAL_DEBUG_SUFFIX})
    endif()
elseif (ONEDAL_USE_DPCPP STREQUAL "yes" AND ONEDAL_INTERFACE STREQUAL "yes")
    list(APPEND DAL_LIBS onedal_dpc${DAL_DEBUG_SUFFIX})
    if(MKL_DEPENDENCY STREQUAL "yes")
        list(APPEND oneDAL_IMPORTED_TARGETS MKL::mkl_sycl${DAL_DEBUG_SUFFIX})
    endif()
    if(ONEDAL_USE_PARAMETERS_LIBRARY)
        list(APPEND DAL_LIBS onedal_parameters_dpc${DAL_DEBUG_SUFFIX})
    endif()
endif()

list(APPEND DAL_LIBS onedal_core${DAL_DEBUG_SUFFIX})

set(THREADING_TYPE "parallel")
set(DAL_THREAD_LIBRARY_NAME onedal_thread${DAL_DEBUG_SUFFIX})

if (ONEDAL_SET_TBB_MANUALLY STREQUAL "no")
    find_package(TBB REQUIRED tbb tbbmalloc)
    if (NOT TBB_IMPORTED_TARGETS)
        # The TBB_IMPORTED_TARGETS is not set if a TBBConfig.cmake from a source
        # build of oneTBB is found. Add the extra imported lib targets
        list(APPEND oneDAL_IMPORTED_TARGETS TBB::tbb TBB::tbbmalloc)
    else()
        # TBB_IMPORTED_ARGETS is set when oneTBB is downloaded with the
        # dev/download_tbb.sh script
        list(APPEND oneDAL_IMPORTED_TARGETS ${TBB_IMPORTED_TARGETS})
    endif()
endif()

if(UNIX)
    if(APPLE)
        list(APPEND oneDAL_IMPORTED_TARGETS -lc++)
    else()
        list(APPEND oneDAL_IMPORTED_TARGETS -lstdc++)
    endif()
endif()

if (UNIX OR ONEDAL_LINK STREQUAL "static")
    list(APPEND DAL_LIBS ${DAL_THREAD_LIBRARY_NAME})
endif()

if(UNIX)
    if(NOT APPLE)
        list(APPEND oneDAL_IMPORTED_TARGETS -lm -lpthread)
    endif()
    list(APPEND oneDAL_IMPORTED_TARGETS -ldl)
endif()

if (ONEDAL_USE_DPCPP STREQUAL "yes")
    if (UNIX)
        list(APPEND oneDAL_IMPORTED_TARGETS -lOpenCL)
    else()
        set(CMAKE_EXE_LINKER_FLAGS "-fsycl -fsycl-device-code-split=per_kernel ${CMAKE_EXE_LINKER_FLAGS}")
        list(APPEND oneDAL_IMPORTED_TARGETS OpenCL.lib)
    endif()
    set(CMAKE_CXX_FLAGS "-fsycl -fsycl-device-code-split=per_kernel ${CMAKE_CXX_FLAGS}")
endif()

if (ONEDAL_INTERFACE STREQUAL "yes")
    if (MSVC)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17")
    else ()
        set(CMAKE_CXX_STANDARD 17)
    endif()
endif()

if(UNIX)
    if(APPLE)
        set(LIB_PREFIX "lib")
        set(LIB_EXT ".a")
        set(DLL_EXT ".${_dal_ver_major_bin}.dylib")
    else()
        set(LIB_PREFIX "lib")
        set(LIB_EXT ".a")
        set(DLL_EXT ".so.${_dal_ver_major_bin}")
    endif()
else()
    set(LIB_PREFIX "")
    set(LIB_EXT ".lib")
    set(DLL_EXT "_dll.${_dal_ver_major_bin}.lib")
endif()

foreach (_dal_component ${DAL_LIBS})
    set(oneDAL_FIND_REQUIRED_${_dal_component} 1)
endforeach()

foreach (_dal_component ${DAL_LIBS})
    if (TARGET oneDAL::${_dal_component})
        continue()
    endif()

    set(oneDAL_${_dal_component}_FOUND 0)

    if (${ONEDAL_LINK} STREQUAL "static")
        add_library(oneDAL::${_dal_component} STATIC IMPORTED)
        find_library(
            _dal_lib
            NAMES "${LIB_PREFIX}${_dal_component}${LIB_EXT}"
            PATH_SUFFIXES "lib/@ARCH_DIR_ONEDAL@"
            PATHS "${_dal_root}")
    elseif (${ONEDAL_LINK} STREQUAL "dynamic")
        add_library(oneDAL::${_dal_component} SHARED IMPORTED)
        find_library(
            _dal_lib
            NAMES "${LIB_PREFIX}${_dal_component}${DLL_EXT}"
            PATH_SUFFIXES "lib/@ARCH_DIR_ONEDAL@"
            PATHS "${_dal_root}")
    endif()

    get_filename_component(_dal_include_dir "${_dal_root}/include" ABSOLUTE)
    set_target_properties(oneDAL::${_dal_component} PROPERTIES
                            INTERFACE_INCLUDE_DIRECTORIES "${_dal_include_dir}")
    unset(_dal_include_dir)

    if (WIN32 AND ${ONEDAL_LINK} STREQUAL "dynamic")
        find_file(${_dal_component}_dll ${_dal_component}.${_dal_ver_major_bin}.dll PATHS "${_dal_root}/@DLL_REL_PATH@")
        set_target_properties(oneDAL::${_dal_component} PROPERTIES
                              IMPORTED_LOCATION "${${_dal_component}_dll}"
                              IMPORTED_IMPLIB "${_dal_lib}")
    else()
        set_target_properties(oneDAL::${_dal_component} PROPERTIES
                              IMPORTED_LOCATION "${_dal_lib}")
    endif()

    if (_dal_lib)
        list(APPEND DAL_LIBRARIES oneDAL::${_dal_component})
        set(oneDAL_${_dal_component}_FOUND 1)
    elseif (oneDAL_FIND_REQUIRED AND oneDAL_FIND_REQUIRED_${_dal_component})
        message(STATUS "Missed required DAL component: ${_dal_component}")
        message(STATUS "  ${_dal_lib} must exist.")
        set(oneDAL_FOUND FALSE)
    endif()

    unset(_dal_lib CACHE)
endforeach()

list(APPEND oneDAL_IMPORTED_TARGETS ${DAL_LIBRARIES})
list(REMOVE_DUPLICATES oneDAL_IMPORTED_TARGETS)

set(oneDAL_ROOT_DIR "${_dal_root}")
set(oneDAL_INCLUDE_DIRS "${_dal_root}/@INC_REL_PATH@")
if (WIN32 AND ${ONEDAL_LINK} STREQUAL "dynamic")
    set(oneDAL_DLL_DIR "${_dal_root}/@DLL_REL_PATH@")
endif()

if(oneDAL_FOUND STREQUAL "FALSE")
    message(
        FATAL_ERROR
        "oneDAL was not found in ${oneDAL_ROOT_DIR}! Set/check DALROOT environment variable!"
    )
else()
    message(STATUS "oneDAL_ROOT_DIR:         ${_dal_root}")
    message(STATUS "oneDAL_INCLUDE_DIRS:     ${_dal_root}/@INC_REL_PATH@")
    if (WIN32 AND ONEDAL_LINK STREQUAL "dynamic")
        message(STATUS "oneDAL_DLL_DIR:          ${_dal_root}/@DLL_REL_PATH@")
    endif()
    message(STATUS "oneDAL_IMPORTED_TARGETS: ${oneDAL_IMPORTED_TARGETS}")
endif()

unset(_dal_lib)
unset(_dal_root)
